#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <ctype.h>



#define NoneRedir   0
#define OutputRedir 1
#define AppendRedir 2
#define InputRedir  3

int redir = NoneRedir;
char *filename = NULL;



char enval[100][100];
int env_num = 0;
int lastcode = 0; // 记录最近一个进程退出是的退出码，默认为0

char cwd[1024]; 
const char* getUserName() {
  const char *username = getenv("USER");
  if (username) {
    return username;
  }
  return "none";
}

const char* getHostName() {
  const char* hostname = getenv("HOSTNAME");
  if (hostname) {
    return hostname;
  }
  return "none";
}

const char* getPwd() {
  const char* pwd = getenv("PWD");
  if (pwd) {
    return pwd;
  }
  return "none";
}
// 处理内建命令
// 成功执行返回1，失败返回0
int dobuildin(char *argv[]) {
  if (strcmp(argv[0], "cd") == 0) {
    char *path = NULL;
    if (NULL == argv[0]) {
      // 如果后面没有跟路径，就让路径默认为'.'即当前目录
      path = ".";
    } else {
      path = argv[1];
    }


    chdir(path);
    // 获取当前进程的绝对路径
    char temp[1024];
    getcwd(temp, sizeof(temp));
    // 将temp写入cwd中
    sprintf(cwd, "PWD=%s", temp);
    // 将cwd中的环境变量导入到系统的环境变量表中
    putenv(cwd);
    return 1;
    } else if (strcmp(argv[0], "export") == 0) {
    if (argv[1] == NULL) {
      return 1;
    }
    strcpy(enval[env_num], argv[1]);
    putenv(enval[env_num]); // 注意这里要导入的是enval[env_num]，而不能是argv[1]
    env_num++;
    return 1;
    } else if (strcmp(argv[0], "echo") == 0) {
      if (argv[1] == NULL) {
        printf("\n");
        return 1;
      }
      if (argv[1][0] == '$' && strlen(argv[1]) > 1) {
        if (argv[1][1] == '?') {
          // 打印上一个进程的退出码
          printf("%d\n", lastcode);
          // 因为内建命令执行时总是成功的，所以这里直接将lastcode设成0就行
          lastcode = 0; 
          
        } else {
          // 打印环境变量
          char *val = argv[1] + 1;
          char *reval = getenv(val);
          if (reval == NULL) {
            printf("\n");
            return 1;
          }
          printf("%s\n", reval);
        }
      } else {
        // 表示是字符串
        printf("%s\n", argv[1]);
      }
      return 1;
    }
  return 0;

}

void checkRedir(char *argv[], int argc) {
  for (int i = argc - 2; i >= 0; i--) {
    if (strcmp(argv[i], ">") == 0) { // 输出重定向
      redir = OutputRedir;
      if (i + 1 < argc - 1) {
        filename = argv[i + 1];
      }
      argv[i] = NULL;
      break;
      
    } else if (strcmp(argv[i], ">>") == 0) { // 追加重定向
      redir = AppendRedir;
      if (i + 1 < argc - 1) {
        filename = argv[i + 1];
      }
      argv[i] = NULL;
      break;

    } else if (strcmp(argv[i], "<") == 0) { // 输入重定向
      redir = InputRedir;
      if (i + 1 < argc - 1) {
        filename = argv[i + 1];
      }
      argv[i] = NULL;
      break;

    }
    
  }

}

int main() {
  char usercommand[1024];
  while (1) {
  redir = NoneRedir;
  filename = NULL;
  char *argv[100] = { NULL };
  int argc = 0;
  printf("[%s@%s %s]￥ ", getUserName(), getHostName(), getPwd());
  char *r = fgets(usercommand, sizeof(usercommand), stdin);
  if (NULL == r || strlen(usercommand) == 0) {
    continue;
  }
  usercommand[strlen(usercommand) - 1] = '\0';

  // 分割用户输入的指令
  argv[argc++] = strtok(usercommand, " ");
  while (argv[argc++] = strtok(NULL, " "));



  // 检查是否有重定向操作
  checkRedir(argv, argc);

  // 检查是否是内建命令并执行
  int res = dobuildin(argv);
  if (res) {
    continue; // 如果成功执行就不用再往后执行了
  }
  
  

  // 执行指令
  pid_t id = fork();
  if (0 == id) {
    // child
    // 在替换程序之前先检查是否有重定向操作
    int fd = 0;
    if (redir == InputRedir) {
      fd = open(filename, O_RDONLY, 0666);
      dup2(fd, 0);
    } else if (redir == OutputRedir) {
      fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
      dup2(fd, 1);
    } else if (redir == AppendRedir) {
      fd = open(filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
      dup2(fd, 1);
    }



    int n = execvp(argv[0], argv);
    if (-1 == n) {
      printf("-myShell: %s: command not found\n", argv[0]);
    }
    exit(1);
  } else {
    // father
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if (rid > 0) {
      lastcode = WEXITSTATUS(status);
    } else {
      return -1;
    }
  }
  }
  return 0;
}
